import numpy as np
from collections import deque
import copy
import events as e

ACTIONS = ['UP', 'RIGHT', 'DOWN', 'LEFT', 'WAIT', 'BOMB']

def vaild_action_filter(game_state):
    """
    filter vaild action
    :game_state The dictionary that describes everything on the board.
    :valid_actions valid action list according to game_state
    """
    x = game_state['self'][3][0]
    y = game_state['self'][3][1]
    field = np.array(game_state["field"]).copy()

    for bomb in game_state["bombs"]:
        (_x, _y), timer = bomb
        field[_x, _y] = 2 # 炸弹
    for other in game_state["others"]:
        name, score, bombs_left, (_x, _y) = other
        field[_x, _y] = 3 # 其他人

    valid_actions = []
    if field[x, y] == 0:
        valid_actions.append(4)
    
    # 能添加炸弹
    if game_state['self'][2]:
        valid_actions.append(5)
    
    # 不能撞墙
    if x >= 1 and field[x - 1,y] == 0:
        valid_actions.append(3)
    if y <= 15 and field[x,y + 1] == 0:
        valid_actions.append(2)
    if x <= 15 and field[x + 1,y] == 0:
        valid_actions.append(1)
    if y >= 1 and field[x,y - 1] == 0:
        valid_actions.append(0)
    
    if len(valid_actions) == 0:
        valid_actions.append(4)

    return valid_actions


def state_to_features(game_state: dict) -> np.array:
    """
    *This is not a required function, but an idea to structure your code.*

    Converts the game state to the input of your model, i.e.
    a feature vector.

    You can find out about the state of the game environment via game_state,
    which is a dictionary. Consult 'get_state_for_agent' in environment.py to see
    what it contains.

    :param game_state:  A dictionary describing the current game board.
    :return: np.array
    """
    # This is the dict before the game begins and after it ends
    if game_state is None:
        return None

    # 将 game_state 编码成 [F, H, W] 的3D向量
    # For example, you could construct several channels of equal shape, ...
    
    H,W = game_state['field'].shape
    channels = np.zeros((12,H,W)) # 每一帧有18条信息
    name, score, bombs_left, (self_x, self_y) = game_state['self']
    # 1. 墙壁 0
    x,y = np.where(game_state['field'] == -1)
    for _x,_y in zip(x,y):
        channels[0,_x,_y] = 1

    # 2. 木箱 1
    x,y = np.where(game_state['field'] == 1)
    for _x,_y in zip(x,y):
        channels[1,_x,_y] = 1

    # 3. 炸弹 2-7
    T = 6
    for bomb in game_state['bombs']:
        (x, y), timer = bomb
        channels[2+timer][x,y] = 1

    # 'UP', 'RIGHT', 'DOWN', 'LEFT'
    direction = [(0,-1),(1,0),(0,1),(-1,0)]
    def _place_invaild(i,j,k):
        if channels[2+k,i,j] != 1:
            return
        if channels[2+k+1,i,j] == 0:
            channels[2+k+1,i,j] = 0.99

        for d in range(4):
            for p in range(1,4):
                # p∈{1,2,3}表示可以连续炸三个位置
                next_x = i + direction[d][0] * p 
                next_y = j + direction[d][1] * p
                # 超出边界不再炸
                if next_x < 0 or next_x >= H or next_y < 0 or next_y >= W:
                    break
                # 遇到墙不再炸
                if channels[1,next_x,next_y] == 1:
                    break
                if channels[2+k,next_x,next_y] == 0:
                    channels[2+k,next_x,next_y] = 0.99
                # 炸弹持续两帧，所以要对下一帧进行设置
                if channels[2+k+1,next_x,next_y] == 0:
                    channels[2+k+1,next_x,next_y] = 0.99

    # 设置爆炸会炸到的区域
    [_place_invaild(i,j,k) for i in range(H) for j in range(W) for k in range(T)]

    # 4. 其他人 8-9
    for other in game_state['others']:
        name, score, bombs_left, (x, y) = other
        channels[8,x, y] = 1
        channels[9,x, y] = score

    # 5. 金币位置 10
    for coin in game_state['coins']:
        channels[10,coin[0],coin[1]] = 1

    # 6. 我的位置 11
    name, score, bombs_left, (x, y) = game_state['self']
    channels[11, self_x, self_y] = 1

    # concatenate them as a feature tensor (they must have the same shape), ...
    # stacked_channels = np.stack(channels,axis=0)
    # and return them as a vector ? why 
    return channels


def event_to_reward(events):

    REWARD_DICT = {
        e.COIN_COLLECTED : 1.0,
        e.KILLED_OPPONENT : 5.0,
        e.KILLED_SELF : -5.0,
        e.GOT_KILLED : -5.0,
        e.WAITED : -0.1,
    }
    r = 0.0
    
    for _e in events:
        r += REWARD_DICT.get(_e, 0.0)
    return r

    



    